/**************************************************************************************************
* Title: SumMatrix.inl
* Author: Gael Huber
* Description: Contains inline definition of 4x4 Matrix functions
**************************************************************************************************/

/**************************************************************************************************
* Default constructor
**************************************************************************************************/
inline Matrix::Matrix(void) 
	:	_11(0.0f), _12(0.0f), _13(0.0f), _14(0.0f),
		_21(0.0f), _22(0.0f), _23(0.0f), _24(0.0f),
		_31(0.0f), _32(0.0f), _33(0.0f), _34(0.0f),
		_41(0.0f), _42(0.0f), _43(0.0f), _44(0.0f)
{ }

/**************************************************************************************************
* Constructor from array of floats
**************************************************************************************************/
inline Matrix::Matrix(const float* f)
	:	_11(f[0]), _12(f[1]), _13(f[2]), _14(f[3]),
		_21(f[4]), _22(f[5]), _23(f[6]), _24(f[7]),
		_31(f[8]), _32(f[9]), _33(f[10]), _34(f[11]),
		_41(f[12]), _42(f[13]), _43(f[14]), _44(f[15])

{ }

/**************************************************************************************************
* Constructor where all terms are specified
**************************************************************************************************/
inline Matrix::Matrix(	
	float _11, float _12, float _13, float _14,
	float _21, float _22, float _23, float _24,
	float _31, float _32, float _33, float _34,
	float _41, float _42, float _43, float _44 ) 
	:	_11(_11), _12(_12), _13(_13), _14(_14),
		_21(_21), _22(_22), _23(_23), _24(_24),
		_31(_31), _32(_32), _33(_33), _34(_34),
		_41(_41), _42(_42), _43(_43), _44(_44)
{ }

/**************************************************************************************************
* Copy constructor
**************************************************************************************************/
inline Matrix::Matrix(const Matrix& rhs)
	:	_11(rhs._11), _12(rhs._12), _13(rhs._13), _14(rhs._14),
		_21(rhs._21), _22(rhs._22), _23(rhs._23), _24(rhs._24),
		_31(rhs._31), _32(rhs._32), _33(rhs._33), _34(rhs._34),
		_41(rhs._41), _42(rhs._42), _43(rhs._43), _44(rhs._44)
{ }

/**************************************************************************************************
* Return a given position in the matrix, mutable value
**************************************************************************************************/
inline float& Matrix::operator() (unsigned int row, unsigned int col) {
	return m[row][col];
}

/**************************************************************************************************
* Return a given position in the matrix, immutable value
**************************************************************************************************/
inline float Matrix::operator() (unsigned int row, unsigned int col) const {
	return m[row][col];
}

/**************************************************************************************************
* Return a non-const version of the object
**************************************************************************************************/
inline Matrix::operator float* (void) {
	return (float*) this;
}

/**************************************************************************************************
* Return a const version of the object
**************************************************************************************************/
inline Matrix::operator const float* (void) const {
	return (const float*) this;
}

/**************************************************************************************************
* Assignment operator
**************************************************************************************************/
inline Matrix& Matrix::operator=(const Matrix& rhs) {
	// Self assignment guard
	if(this == &rhs)
		return *this;
	
	_11 = rhs._11;	_12 = rhs._12;	_13 = rhs._13;	_14 = rhs._14;
	_21 = rhs._21;	_22 = rhs._22;	_23 = rhs._23;	_24 = rhs._24;
	_31 = rhs._31;	_32 = rhs._32;	_33 = rhs._33;	_34 = rhs._34;
	_41 = rhs._41;	_42 = rhs._42;	_43 = rhs._43;	_44 = rhs._44;
	return *this;
}

/**************************************************************************************************
* Returns the sum of two matrices
**************************************************************************************************/
inline Matrix& Matrix::operator+=(const Matrix& rhs) {
	if(this == &rhs)
		return *this;

	_11 += rhs._11;	_12 += rhs._12;	_13 += rhs._13;	_14 += rhs._14;
	_21 += rhs._21;	_22 += rhs._22;	_23 += rhs._23;	_24 += rhs._24;
	_31 += rhs._31;	_32 += rhs._32;	_33 += rhs._33;	_34 += rhs._34;
	_41 += rhs._41;	_42 += rhs._42;	_43 += rhs._43;	_44 += rhs._44;
	return *this;
}

/**************************************************************************************************
* Returns the difference between two matrices
**************************************************************************************************/
inline Matrix& Matrix::operator-=(const Matrix& rhs) {
	_11 -= rhs._11;	_12 -= rhs._12;	_13 -= rhs._13;	_14 -= rhs._14;
	_21 -= rhs._21;	_22 -= rhs._22;	_23 -= rhs._23;	_24 -= rhs._24;
	_31 -= rhs._31;	_32 -= rhs._32;	_33 -= rhs._33;	_34 -= rhs._34;
	_41 -= rhs._41;	_42 -= rhs._42;	_43 -= rhs._43;	_44 -= rhs._44;
	return *this;
}

/**************************************************************************************************
* Multiplies the matrix by a scalar value
**************************************************************************************************/
inline Matrix& Matrix::operator*=(float rhs) {
	_11 *= rhs;	_12 *= rhs;	_13 *= rhs;	_14 *= rhs;
	_21 *= rhs;	_22 *= rhs;	_23 *= rhs;	_24 *= rhs;
	_31 *= rhs;	_32 *= rhs;	_33 *= rhs;	_34 *= rhs;
	_41 *= rhs;	_42 *= rhs;	_43 *= rhs;	_44 *= rhs;
	return *this;
}
/**************************************************************************************************
* Divides the matrix by a scalar value
**************************************************************************************************/
inline Matrix& Matrix::operator/=(float rhs) {
	const float f = 1.0f / rhs;
	_11 *= f;	_12 *= f;	_13 *= f;	_14 *= f;
	_21 *= f;	_22 *= f;	_23 *= f;	_24 *= f;
	_31 *= f;	_32 *= f;	_33 *= f;	_34 *= f;
	_41 *= f;	_42 *= f;	_43 *= f;	_44 *= f;
	return *this;
}

/**************************************************************************************************
* Returns a positive version of the matrix
**************************************************************************************************/
inline Matrix Matrix::operator+(void) const {
	return Matrix(*this);
}

/**************************************************************************************************
* Return a negated version of the matrix
**************************************************************************************************/
inline Matrix Matrix::operator-(void) const {
	return Matrix(
		-_11, -_12, -_13, -_14,
		-_21, -_22, -_23, -_24,
		-_31, -_32, -_33, -_34,
		-_41, -_42, -_43, -_44);
}

/**************************************************************************************************
* Return the sum of two matrices
**************************************************************************************************/
inline Matrix Matrix::operator+(const Matrix& rhs) const {
	return Matrix(
		_11 + rhs._11, _12 + rhs._12, _13 + rhs._13, _14 + rhs._14,
		_21 + rhs._21, _22 + rhs._22, _23 + rhs._23, _24 + rhs._24,
		_31 + rhs._31, _32 + rhs._32, _33 + rhs._33, _34 + rhs._34,
		_41 + rhs._41, _42 + rhs._42, _43 + rhs._43, _44 + rhs._44);
}

/**************************************************************************************************
* Return the difference between two matrices
**************************************************************************************************/
inline Matrix Matrix::operator-(const Matrix& rhs) const {
	return Matrix(
		_11 - rhs._11, _12 - rhs._12, _13 - rhs._13, _14 - rhs._14,
		_21 - rhs._21, _22 - rhs._22, _23 - rhs._23, _24 - rhs._24,
		_31 - rhs._31, _32 - rhs._32, _33 - rhs._33, _34 - rhs._34,
		_41 - rhs._41, _42 - rhs._42, _43 - rhs._43, _44 - rhs._44);
}

/**************************************************************************************************
* Return the product of a matrix and a scalar value
**************************************************************************************************/
inline Matrix Matrix::operator*(float rhs) const {
	return Matrix(
		_11 * rhs, _12 * rhs, _13 * rhs, _14 * rhs,
		_21 * rhs, _22 * rhs, _23 * rhs, _24 * rhs,
		_31 * rhs, _32 * rhs, _33 * rhs, _34 * rhs,
		_41 * rhs, _42 * rhs, _43 * rhs, _44 * rhs);
}

/**************************************************************************************************
* Return the a matrix divided by a scalar value
**************************************************************************************************/
inline Matrix Matrix::operator/(float rhs) const {
	const float f = 1.0f / rhs;
	return Matrix(
		_11 * f, _12 * f, _13 * f, _14 * f,
		_21 * f, _22 * f, _23 * f, _24 * f,
		_31 * f, _32 * f, _33 * f, _34 * f,
		_41 * f, _42 * f, _43 * f, _44 * f);
}

/**************************************************************************************************
* Return the product of a matrix and a scalar value
**************************************************************************************************/
inline Matrix operator*(float lhs, const Matrix& rhs) {
	return Matrix(
		rhs._11 * lhs, rhs._12 * lhs, rhs._13 * lhs, rhs._14 * lhs,
		rhs._21 * lhs, rhs._22 * lhs, rhs._23 * lhs, rhs._24 * lhs,
		rhs._31 * lhs, rhs._32 * lhs, rhs._33 * lhs, rhs._34 * lhs,
		rhs._41 * lhs, rhs._42 * lhs, rhs._43 * lhs,rhs. _44 * lhs);
}

/**************************************************************************************************
* Equivalence
**************************************************************************************************/
inline bool Matrix::operator==(const Matrix& rhs) const {
	return (_11 == rhs._11 && _12 == rhs._12 && _13 == rhs._13 && _14 == rhs._14 &&
			_21 == rhs._21 && _22 == rhs._22 && _23 == rhs._23 && _24 == rhs._24 &&
			_31 == rhs._31 && _32 == rhs._32 && _33 == rhs._33 && _34 == rhs._34 &&
			_41 == rhs._41 && _42 == rhs._42 && _43 == rhs._43 && _44 == rhs._44);
}

/**************************************************************************************************
* Non-equivalence
**************************************************************************************************/
inline bool Matrix::operator!=(const Matrix& rhs) const {
	return (_11 != rhs._11 || _12 != rhs._12 || _13 != rhs._13 || _14 != rhs._14 ||
			_21 != rhs._21 || _22 != rhs._22 || _23 != rhs._23 || _24 != rhs._24 ||
			_31 != rhs._31 || _32 != rhs._32 || _33 != rhs._33 || _34 != rhs._34 ||
			_41 != rhs._41 || _42 != rhs._42 || _43 != rhs._43 || _44 != rhs._44);
}

/**************************************************************************************************
* Return identity matrix
**************************************************************************************************/
inline Matrix* MatrixIdentity(Matrix* pOut) {
	pOut->_11 = 1.0f; pOut->_12 = 0.0f; pOut->_13 = 0.0f; pOut->_14 = 0.0f;
	pOut->_21 = 0.0f; pOut->_22 = 1.0f; pOut->_23 = 0.0f; pOut->_24 = 0.0f;
	pOut->_31 = 0.0f; pOut->_32 = 0.0f; pOut->_33 = 1.0f; pOut->_34 = 0.0f;
	pOut->_41 = 0.0f; pOut->_42 = 0.0f; pOut->_43 = 0.0f; pOut->_44 = 1.0f;
	return pOut;
}

/**************************************************************************************************
* Is the matrix an identity matrix?
**************************************************************************************************/
inline bool MatrixIsIdentity(const Matrix* pM) {
	return (pM->_11 == 1.0f && pM->_12 == 0.0f && pM->_13 == 0.0f && pM->_14 == 0.0f &&
			pM->_21 == 0.0f && pM->_22 == 1.0f && pM->_23 == 0.0f && pM->_24 == 0.0f &&
			pM->_31 == 0.0f && pM->_32 == 0.0f && pM->_33 == 1.0f && pM->_34 == 0.0f &&
			pM->_41 == 0.0f && pM->_42 == 0.0f && pM->_43 == 0.0f && pM->_44 == 1.0f);
}

/**************************************************************************************************
* Sum of two matrices
**************************************************************************************************/
inline Matrix* MatrixAdd(Matrix* pOut, const Matrix* pM1, const Matrix* pM2) {
	pOut->_11 = pM1->_11 + pM2->_11;	pOut->_12 = pM1->_12 + pM2->_12;
	pOut->_13 = pM1->_13 + pM2->_13;	pOut->_14 = pM1->_14 + pM2->_14;
	pOut->_21 = pM1->_21 + pM2->_21;	pOut->_22 = pM1->_22 + pM2->_22;
	pOut->_23 = pM1->_23 + pM2->_23;	pOut->_24 = pM1->_24 + pM2->_24;
	pOut->_31 = pM1->_31 + pM2->_31;	pOut->_32 = pM1->_32 + pM2->_32;
	pOut->_33 = pM1->_33 + pM2->_33;	pOut->_34 = pM1->_34 + pM2->_34;
	pOut->_41 = pM1->_41 + pM2->_41;	pOut->_42 = pM1->_42 + pM2->_42;
	pOut->_43 = pM1->_43 + pM2->_43;	pOut->_44 = pM1->_44 + pM2->_44;
	return pOut;
}

/**************************************************************************************************
* Difference between two matrices
**************************************************************************************************/
inline Matrix* MatrixSub(Matrix* pOut, const Matrix* pM1, const Matrix* pM2) {
	pOut->_11 = pM1->_11 - pM2->_11;	pOut->_12 = pM1->_12 - pM2->_12;
	pOut->_13 = pM1->_13 - pM2->_13;	pOut->_14 = pM1->_14 - pM2->_14;
	pOut->_21 = pM1->_21 - pM2->_21;	pOut->_22 = pM1->_22 - pM2->_22;
	pOut->_23 = pM1->_23 - pM2->_23;	pOut->_24 = pM1->_24 - pM2->_24;
	pOut->_31 = pM1->_31 - pM2->_31;	pOut->_32 = pM1->_32 - pM2->_32;
	pOut->_33 = pM1->_33 - pM2->_33;	pOut->_34 = pM1->_34 - pM2->_34;
	pOut->_41 = pM1->_41 - pM2->_41;	pOut->_42 = pM1->_42 - pM2->_42;
	pOut->_43 = pM1->_43 - pM2->_43;	pOut->_44 = pM1->_44 - pM2->_44;
	return pOut;
}